﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

using ECCIT;

namespace ECCIT.Xml
{
    public class ExcelXml
    {
        private const string C_NODE_PREFIX = "ss";
        private const string C_NAMESPACE_URI = "urn:schemas-microsoft-com:office:spreadsheet";

        public string FilePath { get; set; }
        public XmlDocument Document { get; set; }
        public XmlNodeList SheetNodes { get; set; }
        public XmlNamespaceManager NamespaceManager { get; set; }
        public XmlNode StylesNode { get; set; }

        public ExcelXml(string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException("The excel xml file does not exist.", filePath);

            try
            {
                this.FilePath = filePath;
                this.Document = new XmlDocument();
                this.Document.Load(filePath);

                XmlNameTable nameTable = this.Document.NameTable;
                this.NamespaceManager = new XmlNamespaceManager(nameTable);
                this.NamespaceManager.AddNamespace(C_NODE_PREFIX, C_NAMESPACE_URI);

                this.SheetNodes = SelectNodes("./Workbook/Worksheet");
                this.StylesNode = SelectSingleNode("./Workbook/Styles");
            }
            catch (Exception ex)
            {
            
            }
        }

        private string GetPrefixedXPath(string xPath)
        {
            JoinedString xPaths = new JoinedString(xPath, "/");
            int count = xPaths.Count;
            if (count == 0)
                return xPath;

            StringBuilder pathBuilder = new StringBuilder();
            for (int i = 0; i < count; i++)
            {
                if (xPaths[i] == "." || xPaths[i] == ".." || xPaths[i] == string.Empty)
                {
                    pathBuilder.Append(xPaths[i]);
                }
                else
                {
                    if (i > 0)
                        pathBuilder.Append("/");
                        
                    pathBuilder.Append(string.Format("{0}:{1}", C_NODE_PREFIX, xPaths[i]));
                }
            }

            string prefixedXPath = pathBuilder.ToString();
            return prefixedXPath;
        }

        public XmlNodeList SelectNodes(string xPath)
        {
            string prefixedXPath = GetPrefixedXPath(xPath);
            return this.Document.SelectNodes(prefixedXPath, this.NamespaceManager);
        }

        public XmlNode SelectSingleNode(string xPath)
        {
            string prefixedXPath = GetPrefixedXPath(xPath);
            return this.Document.SelectSingleNode(prefixedXPath, this.NamespaceManager);
        }

        public XmlNodeList SelectNodes(XmlNode rootNode, string xPath)
        {
            if (rootNode == null)
                return null;

            string prefixedXPath = GetPrefixedXPath(xPath);
            return rootNode.SelectNodes(prefixedXPath, this.NamespaceManager);
        }

        public XmlNode SelectSingleNode(XmlNode rootNode, string xPath)
        {
            if (rootNode == null)
                return null;

            string prefixedXPath = GetPrefixedXPath(xPath);
            return rootNode.SelectSingleNode(prefixedXPath, this.NamespaceManager);
        }

        public XmlNode GetAttributeNode(XmlNode node, string attributeName)
        {
            if (node == null)
                return null;

            string prefixedXPath = GetPrefixedXPath(attributeName);
            return node.Attributes.GetNamedItem(prefixedXPath);
        }

        public ResultInfo AddHighlightStyleNode(string currentStyleID)
        {
            ResultInfo result = ResultInfo.CreateInstance(false, "");

            try
            {
                // if this style node has been existed, do nothing
                if (SelectSingleNode (this.StylesNode, string.Format ("Style[@ss:ID='{0}']", currentStyleID + "_h")) != null)
                {
                    result.Result = true;
                    return result;
                }

                XmlNode highlightStyleNode = CreateHighlightStyleNode (currentStyleID);
                this.StylesNode.AppendChild (highlightStyleNode);

                result.Result = true;
            }
            catch (Exception ex)
            {
                result.Message = string.Format("Error occurred in {0}.AddHighlightStyleNode while adding highlighted Style node. Message: {1}", this.GetType().FullName, ex.Message);
            }
            return result;
        }

        public XmlNode CreateHighlightStyleNode(string currentStyleID)
        {
            // below is the node flat xml
            /*
             *  <Style ss:ID="s84">
                    <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
                    <Borders>
                        <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
                        <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
                        <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
                        <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
                    </Borders>
                    <Interior ss:Color="#FF0000" ss:Pattern="Solid"/>
                </Style>  
             * */
            XmlNode styleIDNode = null;
            XmlNode highlightStyleNode = null;
            foreach (XmlNode styleNode in this.StylesNode.ChildNodes)
            {
                styleIDNode = GetAttributeNode(styleNode, "ID");
                if (styleIDNode == null)
                    continue;

                if (styleIDNode.Value == currentStyleID)
                {
                    highlightStyleNode = styleNode.CloneNode (true);
                    break;
                }
            }

            if (highlightStyleNode == null)
                return null;

            styleIDNode = GetAttributeNode (highlightStyleNode, "ID");
            styleIDNode.Value = currentStyleID + "_h";

            XmlNode interiorNode = SelectSingleNode(highlightStyleNode, "Interior");
            if (interiorNode == null)
                interiorNode = CreateNode ("Interior");

            XmlNode colorAttr = CreateAttribute ("Color", "#FF0000");
            XmlNode patternAttr = CreateAttribute ("Pattern", "Solid");
            interiorNode.Attributes.SetNamedItem (colorAttr);
            interiorNode.Attributes.SetNamedItem (patternAttr);
            highlightStyleNode.AppendChild (interiorNode);

            return highlightStyleNode;
        }

        public XmlNode CreateNode (string nodeName)
        {
            XmlNode node = this.Document.CreateNode (XmlNodeType.Element, null, nodeName, C_NAMESPACE_URI);
            return node;
        }

        public XmlNode CreateAttribute (string nodeName, string nodeValue)
        {
            XmlNode attr = this.Document.CreateNode (XmlNodeType.Attribute, "ss", nodeName, C_NAMESPACE_URI);
            attr.Value = nodeValue;
            return attr;
        }
    }
}
